#include "StdAfx.h"
#include "Config.h"

namespace KeyAss 
{
	using namespace std;

	typedef vector<string> StringVec;
	typedef map<string, value> ShortFullMap;

	static const string split_item_end("%%");
	static const string split_item_sep("^^");
	static const char* cfgFile = "XCHG.TXT";
	static ShortFullMap Short2Full;

	value valEmpty;
	std::string empty;

	StringVec Split(string str, const string& delimiters)
	{
		StringVec elems;
		size_t found;
		found = str.find_first_of(delimiters);
		while(found != string::npos){
			if(found > 0){
				elems.push_back(str.substr(0,found));
			}else{
				elems.push_back("");
			}
			str = str.substr(found+delimiters.length());
			found = str.find_first_of(delimiters);
		}
		if(str.length() > 0){
			elems.push_back(str);
		}
		return elems;
	}

	void trim(string& str)
	{
		str.erase(0, str.find_first_not_of("\n \t\r"));       //prefixing spaces
		str.erase(str.find_last_not_of("\n \t\r")+1);         //surfixing spaces
	}

	size_t GetFileSize(string fileName)
	{
		std::ifstream ifs(fileName.c_str(), std::ios::binary | std::ios::ate);
		int file_length = (int)ifs.tellg();
		return file_length;
	}

	Config::Config(void)
	{

	}

	Config& Config::get()
	{
		static Config c;
		return c;
	}

	const value& Config::GetFull(const std::string& shrt)
	{
		string key(shrt.c_str());
		ShortFullMap::iterator itor = Short2Full.find(key);
		if(itor == Short2Full.end()) return valEmpty;
		return itor->second;
	}

	std::string GetKeyVal(const StringVec& keyVal, int idx)
	{
		if((int)keyVal.size() > idx)
		{
			std::string val = keyVal[idx];
			trim(val);
			return val;
		}
		return empty;
	}

	std::string value::ParseKey(const std::string& str)
	{
		_ctrl = SPACE;
		bool hasCtrl = false;
		if(str[0] == '$'){
			if(str.length() >= 2){
				if(str[1] == '<'){
					_ctrl = RETURN;
					hasCtrl = true;
				}
			}
		}

		if(hasCtrl){
			std::string key;
			key = &(str.c_str()[2]);
			return key;
		}
		return str;
	}

	void Config::Load()
	{
		Short2Full.clear();
		ifstream ifs(cfgFile);
		std::stringstream buffer;
		buffer << ifs.rdbuf();
		std::string contents(buffer.str());
		StringVec items = Split(contents, split_item_end);
		for each(string item in items)
		{
			StringVec keyVal = Split(item, split_item_sep);
			if(keyVal.size() != 2) continue;
			value v;
			v.SetLine(GetKeyVal(keyVal, 1));
			std::string key = v.ParseKey(GetKeyVal(keyVal, 0));
			Short2Full[key] = v;
		}
	}

	//-------------------------------------------------------------
	void Replace(std::string& str, const std::string& find, int sub)
	{
		size_t pos = str.find(find);
		if(pos == std::string::npos) return;
		std::stringstream strm;
		if(sub < 10) strm << 0;
		strm << sub;
		std::string subStr(strm.str());
		str.replace(pos, find.size(), subStr);
	}

	void VariableTime(std::string& line)
	{
		static std::string Year("$Y");
		static std::string Month("$M");
		static std::string Day("$D");
		static std::string Hour("$H");
		static std::string Min("$N");
		static std::string Sec("$S");

		time_t thetime;
		time(&thetime);
		tm _tm;
		localtime_s(&_tm, &thetime); 
		
		Replace(line, Year, 1900 + _tm.tm_year);
		Replace(line, Month, _tm.tm_mon+1);
		Replace(line, Day, _tm.tm_mday);
		Replace(line, Hour, _tm.tm_hour);
		Replace(line, Min, _tm.tm_min);
		Replace(line, Sec, _tm.tm_sec);
	}

	void value::SetLine(const std::string& str)
	{
		if(!str.length()) return;
		_lineFull = _line;
		_line = str;
	}

	bool value::IsCtrlKey(DWORD vk) const
	{
		switch(_ctrl)
		{
		case ALL: return true;
		case RETURN: return vk == VK_RETURN;
		case SPACE: return (vk == VK_SEMCOLON) || (vk ==VK_SPACE);
		}
		return false;
	}

	const std::string& value::GetLine() const
	{
		_lineFull = _line;
		VariableTime(_lineFull);
		SetAppend();

		//set cursor at last.
		SetCursor();
		return _lineFull;
	}

	void value::SetCursor() const
	{
		static std::string Cursor("$C");
		size_t pos = _lineFull.find(Cursor);
		if(pos == std::string::npos){
			_curpos = 0;
			return;
		}
		_lineFull.replace(pos, 2, empty);
		_curpos = _lineFull.length() - pos;
	}

	void value::SetAppend() const
	{
		_append = true;
		static std::string DoNotApp("<");
		size_t pos = _lineFull.find_last_of(DoNotApp);
		
		if(pos == _lineFull.length() - 1)
		{
			_lineFull.replace(pos, 1, empty);
			_append = false;
		}
	}

}//namespace
